package com.nstskj.study.netty.tcp.netty.bootstrap;

import cn.hutool.core.thread.ThreadUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.NettyRuntime;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ThreadFactory;

/**
 * @author ZhouChuGang
 * @version 1.0
 * @project nstskj-study-netty-tcp-spring
 * @date 2021/4/20 20:43
 * @Description tcp服务器
 */
@Slf4j
public class NettyTcpBootstrapService {

    /**
     * 主线程池及工作线程
     */
    private final ThreadFactory bossThreadNameFactory;
    private final ThreadFactory workerThreadNameFactory;

    //端口
    private final int port;

    //管道初始化操作
    private final ChannelInitializer<NioSocketChannel> channelInitializer;

    //工作线程
    private NioEventLoopGroup bossGroup;
    private NioEventLoopGroup workerGroup;

    //boss线程异步
    private ChannelFuture serverChannelFuture;

    public NettyTcpBootstrapService(String bossTreadName, String workThreadName, int port, ChannelInitializer<NioSocketChannel> channelInitializer) {
        this.bossThreadNameFactory = ThreadUtil.newNamedThreadFactory(bossTreadName, true);
        this.workerThreadNameFactory = ThreadUtil.newNamedThreadFactory(workThreadName, true);
        this.port = port;
        this.channelInitializer = channelInitializer;
    }

    /**
     * 启动服务
     *
     * @return
     * @throws Exception
     */
    public boolean startService() throws Exception {
        this.bossGroup = new NioEventLoopGroup(1, bossThreadNameFactory);
        this.workerGroup = new NioEventLoopGroup(NettyRuntime.availableProcessors() * 2, workerThreadNameFactory);
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap()
                    .group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    //重用地址
                    .childOption(ChannelOption.SO_REUSEADDR, true)
                    .childOption(ChannelOption.SO_RCVBUF, 65536)
                    .childOption(ChannelOption.SO_SNDBUF, 65536)
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    //使用堆内内存
                    .childOption(ChannelOption.ALLOCATOR, new PooledByteBufAllocator(false))
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(this.channelInitializer);
            serverChannelFuture = serverBootstrap.bind(this.port).sync();
            //TODO 这里会阻塞main线程，暂时先注释掉
//            serverChannelFuture.channel().closeFuture().sync();
            serverChannelFuture.channel().closeFuture().addListener(ChannelFutureListener.CLOSE);
        } catch (Exception e) {
            log.error("服务器启动失败", e);
            return false;
        }
        return true;
    }

    /**
     * 停止服务
     *
     * @return
     * @throws Exception
     */
    public boolean stopService() throws Exception {
        if (bossGroup != null) {
            bossGroup.shutdownGracefully();
        }
        if (workerGroup != null) {
            workerGroup.shutdownGracefully();
        }
        return true;
    }

}
